/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.yonsei.iwb.smarttv.ner;

import com.hp.hpl.jena.rdf.model.Model;
import edu.yonsei.iwb.smarttv.index.IndexedDatatypeProperties;
import edu.yonsei.iwb.smarttv.index.MainIndexedProperties;
import edu.yonsei.iwb.smarttv.result.NamedEntity;
import edu.yonsei.iwb.smarttv.result.NamedEntityLinkedHashMap;
import edu.yonsei.iwb.smarttv.result.NamedEntitySet;
import edu.yonsei.iwb.smarttv.result.ResultNode;
import edu.yonsei.iwb.smarttv.util.SMARTTVLog;
import edu.yonsei.iwb.smarttv.util.UtilMethodSet;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.Version;
import org.apache.lucene.search.function.ValueSourceQuery;

/**
 *
 * @author Onlyjiny
 */
public class NamedEntityRecognition {

    private final int HIT_PER_PAGE = 10;
    private final String DOCUMENT_FIELD = "uri";
    private Directory index;
    private IndexSearcher searcher;
    private Model model;

    public NamedEntityRecognition(Directory dir, Model model) {
        this.index = dir;
        try {
            searcher = new IndexSearcher(this.index);
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.model = model;
    }

    // Index 검색 결과로부터 매칭된 색인어를 추출하여 반환
    private Vector<String> getMatchedWords(Vector<String> querywords, String[] terms) {
        Vector<String> matchedWordset = new Vector<String>();
        Vector<String> termofDoc = new Vector<String>();
        termofDoc.addAll(Arrays.asList(terms));
        for (int j = 0; j < querywords.size(); j++) {
            if (termofDoc.contains(querywords.get(j))) {
                matchedWordset.add(querywords.get(j));
            }
        }
        return matchedWordset;
    }

    public NamedEntityLinkedHashMap recognizeEntity(Vector<String> words) {
        NamedEntityLinkedHashMap neh = new NamedEntityLinkedHashMap();
        StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_34);
        try {
            QueryPartitionSet qps = new QueryPartitionSet(words);
            QueryPartitionSet orderedQPS = new QueryPartitionSet();
            Query query;
            ValueSourceQuery vsq;
            CustomOrderScoreQuery cosq = null;
            for (int i = 0; i < qps.size(); i++) {
                QueryPartition qp = qps.get(i);
                for (int j = 0; j < qp.getQueryVectorSet().size(); j++) {
                    MainIndexedProperties indexProperties = new MainIndexedProperties();
                    Enumeration<String> keys = indexProperties.keys();
                    Vector<String> vector_subquery = qp.getQueryVectorSet().get(j);
                    String subquery = UtilMethodSet.getConjunctionStringForIndex(vector_subquery);
                    float highestScore = 0;
                    float docscore = 0;
                    ScoreDoc[] best_hits = null;
                    while (keys.hasMoreElements()) {
                        TopScoreDocCollector collector = TopScoreDocCollector.create(this.HIT_PER_PAGE, true);
                        String fieldName = keys.nextElement();
                        query = new QueryParser(Version.LUCENE_34, fieldName, analyzer).parse(subquery);

                        // Order Consistency 반영
                        OrderCheckingFunction func = new OrderCheckingFunction(fieldName, vector_subquery);
                        vsq = new ValueSourceQuery(func);
                        cosq = new CustomOrderScoreQuery(query, vsq);

                        this.searcher.search(cosq, collector);
                        cosq.setStrict(true);
                        ScoreDoc[] hits = collector.topDocs().scoreDocs;
                        if (hits.length != 0) {
                            docscore = hits[0].score;
                            if (highestScore < docscore) {
                                highestScore = docscore;
                                best_hits = hits;
                            }
                        }
                    }
                    Vector<Document> documentSet = new Vector<Document>();
                    Vector<Explanation> explanationSet = new Vector<Explanation>();
                    if (best_hits != null) {
                        for (int k = 0; k < best_hits.length; k++) {
                            int docId = best_hits[k].doc;
                            if (best_hits[k].score == highestScore) {
                                Document d = new Document();
                                Explanation explanation = new Explanation();
                                d = searcher.doc(docId);
                                explanation = searcher.explain(cosq, docId);
                                documentSet.add(d);
                                explanationSet.add(explanation);
                            }
                        }
                        qp.putPartitionScore(subquery, highestScore);
                        qp.putPartitionDocumnment(subquery, documentSet);
                        qp.putPartitionExplanation(subquery, explanationSet);
                    }
                }
                boolean added = false;
                for (int j = 0; j < orderedQPS.size(); j++) {
                    QueryPartition oldQ = orderedQPS.get(j);
                    if (oldQ.getPartitionScore() < qp.getPartitionScore()) {
                        orderedQPS.add(j, qp);
                        added = true;
                        break;
                    }
                }
                if (added == false) {
                    orderedQPS.add(qp);
                }
            }
            QueryPartition finalresult = orderedQPS.get(0);
            SMARTTVLog.log(SMARTTVLog.DEFAULT, "Best Partition: " + finalresult.toString() + "\n");
            for (int j = 0; j < finalresult.getQueryVectorSet().size(); j++) {
                NamedEntitySet nes = new NamedEntitySet();
                String subquery = UtilMethodSet.getConjunctionStringForIndex(finalresult.getQueryVectorSet().get(j));
                Vector<Document> d = finalresult.getResultDocument(subquery);
                if (d != null) {
                    for (int i = 0; i < d.size(); i++) {
                        String indexed_URI = d.get(i).get(this.DOCUMENT_FIELD);
//                        System.out.println("Indexed URI = " + indexed_URI);                        
                        if(indexed_URI.startsWith("genID")){
                            String value = d.get(i).get(IndexedDatatypeProperties.LABEL4DATATYPEPROPERTY);
                            String typeInfo = d.get(i).get("type");
//                            System.out.println("value = " + value);
//                            System.out.println("type = " + typeInfo);
                            ResultNode rn = new ResultNode(typeInfo,"LiteralValue");
                            nes.add(new NamedEntity(indexed_URI, value, rn));
                            neh.put(UtilMethodSet.getStringForIndex(finalresult.getQueryVectorSet().get(j)), nes);
                        }else{
                            nes.add(new NamedEntity(model.createResource(indexed_URI)));
                            neh.put(UtilMethodSet.getStringForIndex(finalresult.getQueryVectorSet().get(j)), nes);
                        }                        
                    }
                } else {
                    neh.put(UtilMethodSet.getStringForIndex(finalresult.getQueryVectorSet().get(j)), nes);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return neh;
    }

    @Override
    protected void finalize() throws Throwable {
        searcher.close();
    }
}
